#ifndef __QTCH_SQLITE3_H__
#define __QTCH_SQLITE3_H__


#include "qtch/db/db.h"
#include "qtch/singleton.h"
#include "qtch/mutex.h"

#include <sqlite3.h>
#include <map>
#include <vector>
#include <unordered_map>
#include <list>

namespace qtch {

class SQLite3Res;
class SQLite3;
class SQLite3Stmt : public IStmt , public std::enable_shared_from_this<SQLite3Stmt>{
friend SQLite3Res;
public:
    typedef std::shared_ptr<SQLite3Stmt> ptr;

    static SQLite3Stmt::ptr Create(std::shared_ptr<SQLite3> db,const std::string & stmt);
    ~SQLite3Stmt();
    virtual int bindInt8(int idx, const int8_t& value) override;
    virtual int bindUint8(int idx, const uint8_t& value) override;
    virtual int bindInt16(int idx, const int16_t& value) override;
    virtual int bindUint16(int idx, const uint16_t& value) override;
    virtual int bindInt32(int idx, const int32_t& value) override;
    virtual int bindUint32(int idx, const uint32_t& value) override;
    virtual int bindInt64(int idx, const int64_t& value) override;
    virtual int bindUint64(int idx, const uint64_t& value) override;
    virtual int bindFloat(int idx, const float& value) override;
    virtual int bindDouble(int idx, const double& value) override;
    virtual int bindString(int idx, const char * value) override;
    virtual int bindString(int idx, const std::string& value) override;
    virtual int bindBlob(int idx, const char* value, int64_t size) override;
    virtual int bindBlob(int idx, const std::string& value) override;
    virtual int bindTime(int idx, const time_t& value) override;
    virtual int bindNull(int idx) override;

    virtual int execute() override;
    virtual int64_t getLastInsertId() override;
    virtual ISQLData::ptr query() override;

    virtual void setSqlSentence(const std::string& sql);

    virtual int getError() override;
    virtual std::string getErrStr() override;

private:
    SQLite3Stmt(std::shared_ptr<SQLite3> db,const std::string & stmt);

private:
    sqlite3_stmt* m_stmt;
    std::string m_sql;
    std::shared_ptr<SQLite3> m_sqlite3;
    int m_error = 0;
    std::string m_errmsg = "OK";
};

class SQLite3Res : public ISQLData {
public:
    typedef std::shared_ptr<SQLite3Res> ptr;
    ~SQLite3Res(){}
    SQLite3Res(std::shared_ptr<SQLite3Stmt> res);
    int getErrno() const override;
    const std::string getErrStr() const override;
    int getDataCount() override;
    int getColumnCount() override;
    int getColumnBytes(int idx) override;
    int getColumnType(int idx) override;
    int getColumnIdx(const std::string& columnName);
    int getColumnIdx(const char* columnName);
    std::string getColumnName(int idx) override;
    bool isNull(int idx) override;
    int8_t getInt8(int idx) override;
    uint8_t getUint8(int idx) override;
    int16_t getInt16(int idx) override;
    uint16_t getUint16(int idx) override;
    int32_t getInt32(int idx) override;
    uint32_t getUint32(int idx) override;
    int64_t getInt64(int idx) override;
    uint64_t getUint64(int idx) override;
    float getFloat(int idx) override;
    double getDouble(int idx) override;
    std::string getString(int idx) override;
    std::string getBlob(int idx) override;
    time_t getTime(int idx) override;
    bool isNull(const std::string& name) override;
    int8_t getInt8(const std::string& name) override;
    uint8_t getUint8(const std::string& name) override;
    int16_t getInt16(const std::string& name) override;
    uint16_t getUint16(const std::string& name) override;
    int32_t getInt32(const std::string& name) override;
    uint32_t getUint32(const std::string& name) override;
    int64_t getInt64(const std::string& name) override;
    uint64_t getUint64(const std::string& name) override;
    float getFloat(const std::string& name) override;
    double getDouble(const std::string& name) override;
    std::string getString(const std::string& name) override;
    std::string getBlob(const std::string& name) override;
    time_t getTime(const std::string& name) override;
    bool next() override;

private:
    bool m_first;
    std::shared_ptr<SQLite3Stmt> m_data;
    std::unordered_map<std::string, int> m_cloumn_name_index;
};



class SQLite3Manager;
class SQLite3 : public IDB ,public std::enable_shared_from_this<SQLite3>{
friend SQLite3Manager;
public:
    typedef std::shared_ptr<SQLite3> ptr;
    enum class Flags{
        READONLY = SQLITE_OPEN_READONLY,
        READWRITE = SQLITE_OPEN_READWRITE,
        CREATE = SQLITE_OPEN_CREATE,

    };
    static SQLite3::ptr Create(const std::string& name, const std::string& filePath, int flag = ((int)Flags::READWRITE | (int)Flags::CREATE));
    static SQLite3* CreateNoPtr(const std::string& name, const std::string& filePath, int flag = ((int)Flags::READWRITE | (int)Flags::CREATE));

    std::shared_ptr<sqlite3> getConn() const {return m_conn;}
    uint64_t getLastUsedTime() const {return m_lastUsedTime;}
    const std::string& getDBName() const {return m_dbname;}
    const std::string& getDBPath() const {return m_dbPath;}


    int64_t getLastInsertId()override;
    IStmt::ptr prepare(const std::string& stmt) override;
    int getError() override;
    std::string getErrStr() override;
    ITransaction::ptr openTransaction(bool auto_commit = false) override;
    ISQLData::ptr query(const char* format, ...) override;
    ISQLData::ptr query(const char* format, va_list ap);
    ISQLData::ptr query(const std::string& sql) override;
    int execute(const char* format, ...) override;
    int execute(const char* format, va_list ap);
    int execute(const std::string& sql) override;

private:
    SQLite3(const std::string& name,sqlite3* conn);
private:
    std::shared_ptr<sqlite3> m_conn;
    std::string m_dbPath;
    std::string m_dbname;
    uint64_t m_lastUsedTime;
    bool m_hasError;

};

class SQLite3Transaction : public ITransaction {
public:
    typedef std::shared_ptr<SQLite3Transaction> ptr;

    static SQLite3Transaction::ptr Create(SQLite3::ptr db,bool autoCommit);
    ~SQLite3Transaction();
    bool begin() override;
    bool commit() override;
    bool rollback() override;

    int execute(const char* format, ...) override;
    int execute(const char* format, va_list ap);
    int execute(const std::string& sql) override;
    int64_t getLastInsertId() override;

    SQLite3::ptr getSQLite3() const {return m_db;}
    bool isAutoCommit() const {return m_autoCommit;}
    bool isFinish() const {return m_isfinish;}
    bool hasError() const {return m_hasError;}

private:
    SQLite3Transaction(SQLite3::ptr db,bool autoCommit);

private:
    SQLite3::ptr m_db;
    bool m_autoCommit;
    bool m_isfinish;
    bool m_hasError;

};


class SQLite3Manager {
public:
    typedef Mutex MutexType;

    SQLite3Manager();
    ~SQLite3Manager();

    SQLite3::ptr get(const std::string& name);
    void registerSQLite3(const std::string& name,const std::string& filePath);

    ISQLData::ptr query(const std::string& name,const char* format, ...);
    ISQLData::ptr query(const std::string& name,const char* format, va_list ap);
    ISQLData::ptr query(const std::string& name,const std::string& sql);
    int execute(const std::string& name,const char* format, ...);
    int execute(const std::string& name,const char* format,va_list ap);
    int execute(const std::string& name,const std::string& sql);

    uint32_t getMaxConn() const {return m_maxConn;}
    void setMaxConn(uint32_t v)  {m_maxConn = v;}


private:
    void freeDB(const std::string& name,SQLite3* m);

private:
    std::map<std::string, std::list<SQLite3*> > m_dbs;
    uint32_t m_maxConn;
    std::map<std::string ,std::string > m_filePaths;
    MutexType m_mutex;

};


typedef qtch::Singleton<SQLite3Manager> SQLite3Mgr;



}

#endif